<?php

//Filename: MockeryABetterWayOfMockingTest.php
require_once '../vendor/autoload.php';

class MockeryVersusPHPUnitGetMockTest extends PHPUnit_Framework_TestCase {

	protected function tearDown() {
		\Mockery::close();
	}

	function testCreateAMockedObject() {
		// With PHPUnit
		$phpunitMock = $this->getMock('AClassToBeMocked');

		// With Mockery
		$mockeryMock = \Mockery::mock('AClassToBeMocked');
	}

	function testExpectOnce() {
		$someObject = new SomeClass();

		// With PHPUnit
		$phpunitMock = $this->getMock('AClassToBeMocked');
		$phpunitMock->expects($this->once())->method('someMethod');
		// Exercise for PHPUnit
		$someObject->doSomething($phpunitMock);

		// With Mockery
		$mockeryMock = \Mockery::mock('AnInexistentClass');
		$mockeryMock->shouldReceive('someMethod')->once();
		// Exercise for Mockery
		$someObject->doSomething($mockeryMock);
	}

	function testExpectMultiple() {
		$someObject = new SomeClass();

		// With PHPUnit 2 times
		$phpunitMock = $this->getMock('AClassToBeMocked');
		$phpunitMock->expects($this->exactly(2))->method('someMethod');
		// Exercise for PHPUnit
		$someObject->doSomething($phpunitMock);
		$someObject->doSomething($phpunitMock);

		// With Mockery 2 times
		$mockeryMock = \Mockery::mock('AnInexistentClass');
		$mockeryMock->shouldReceive('someMethod')->twice();
		// Exercise for Mockery
		$someObject->doSomething($mockeryMock);
		$someObject->doSomething($mockeryMock);

		// With Mockery 3 times
		$mockeryMock = \Mockery::mock('AnInexistentClass');
		$mockeryMock->shouldReceive('someMethod')->times(3);
		// Exercise for Mockery
		$someObject->doSomething($mockeryMock);
		$someObject->doSomething($mockeryMock);
		$someObject->doSomething($mockeryMock);
	}

	function testSimpleReturnValue() {
		$someObject = new SomeClass();
		$someValue = 'some value';

		// With PHPUnit
		$phpunitMock = $this->getMock('AClassToBeMocked');
		$phpunitMock->expects($this->once())->method('someMethod')->will($this->returnValue($someValue));
		// Expect the returned value
		$this->assertEquals($someValue, $someObject->doSomething($phpunitMock));


		// With Mockery
		$mockeryMock = \Mockery::mock('AnInexistentClass');
		$mockeryMock->shouldReceive('someMethod')->once()->andReturn($someValue);
		// Expect the returned value
		$this->assertEquals($someValue, $someObject->doSomething($mockeryMock));
	}

	function testDemonstratePHPUnitCallIndexing() {
		$someObject = new SomeClass();
		$firstValue = 'first value';
		$secondValue = 'second value';

		// With PHPUnit
		$phpunitMock = $this->getMock('AClassToBeMocked');
		$phpunitMock->expects($this->at(0))->method('someMethod')->will($this->returnValue($firstValue));
		$phpunitMock->expects($this->at(1))->method('someMethod')->will($this->returnValue($secondValue));
		// Expect the returned value
		$this->assertEquals($firstValue, $someObject->doSomething($phpunitMock));
		$this->assertEquals($secondValue, $someObject->doSomething($phpunitMock));
	}

	function testDemonstratePHPUnitCallIndexingOnTheSameClass() {
		$someObject = new SomeClass();
		$firstValue = 'first value';
		$secondValue = 'second value';

		// With PHPUnit
		$phpunitMock = $this->getMock('AClassToBeMocked');
		$phpunitMock->expects($this->at(0))->method('someMethod')->will($this->returnValue($firstValue));
		$phpunitMock->expects($this->at(1))->method('someMethod')->will($this->returnValue($secondValue));
		$phpunitMock->expects($this->at(2))->method('someMethod')->will($this->returnValue($firstValue));
		$phpunitMock->expects($this->at(3))->method('someMethod')->will($this->returnValue($secondValue));
		// Expect the returned value
		$this->assertEquals('first value second value', $someObject->concatenate($phpunitMock));
		$this->assertEquals('first value second value', $someObject->concatenate($phpunitMock));
	}

	function testMultipleReturnValuesWithMockery() {
		$someObject = new SomeClass();
		$firstValue = 'first value';
		$secondValue = 'second value';

		// With Mockery
		$mockeryMock = \Mockery::mock('AnInexistentClass');
		$mockeryMock->shouldReceive('someMethod')->andReturn($firstValue, $secondValue, $firstValue, $secondValue);

		// Expect the returned value
		$this->assertEquals('first value second value', $someObject->concatenate($mockeryMock));
		$this->assertEquals('first value second value', $someObject->concatenate($mockeryMock));
	}

	function testPHPUnitIndexingOnMultipleMethods() {
		$someObject = new SomeClass();
		$firstValue = 'first value';
		$secondValue = 'second value';

		// With PHPUnit
		$phpunitMock = $this->getMock('AClassToBeMocked');

		// First and second call on the semeMethod:
		$phpunitMock->expects($this->at(0))->method('someMethod')->will($this->returnValue($firstValue));
		$phpunitMock->expects($this->at(1))->method('someMethod')->will($this->returnValue($secondValue));
		// Expect the returned value
		$this->assertEquals('first value second value', $someObject->concatenate($phpunitMock));

		// First and second call on the anotherMethod:
		$phpunitMock->expects($this->at(0))->method('anotherMethod')->will($this->returnValue($firstValue));
		$phpunitMock->expects($this->at(1))->method('anotherMethod')->will($this->returnValue($secondValue));
		// Expect the returned value
		$this->assertEquals('first value - second value', $someObject->concatWithMinus($phpunitMock));
	}

	function testMultipleReturnValuesForDifferentFunctionsWithMockery() {
		$someObject = new SomeClass();
		$firstValue = 'first value';
		$secondValue = 'second value';

		// With Mockery
		$mockeryMock = \Mockery::mock('AnInexistentClass');
		$mockeryMock->shouldReceive('someMethod')->andReturn($firstValue, $secondValue);
		$mockeryMock->shouldReceive('anotherMethod')->andReturn($firstValue, $secondValue);

		// Expect the returned value
		$this->assertEquals('first value second value', $someObject->concatenate($mockeryMock));
		$this->assertEquals('first value - second value', $someObject->concatWithMinus($mockeryMock));
	}

	function testPHUnitCandDecideByParameter() {
		$someObject = new SomeClass();

		// With PHPUnit
		$phpunitMock = $this->getMock('AClassToBeMocked');
		$phpunitMock->expects($this->any())->method('getNumber')->with(2)->will($this->returnValue(2));
		//$phpunitMock->expects($this->any())->method('getNumber')->with(3)->will($this->returnValue(3));

		$this->assertEquals(4, $someObject->doubleNumber($phpunitMock, 2));
		//$this->assertEquals(6, $someObject->doubleNumber($phpunitMock, 3));

	}

	function testMockeryReturningDifferentValuesBasedOnParameter() {
		$someObject = new SomeClass();

		// Mockery
		$mockeryMock = \Mockery::mock('AnInexistentClass');
		$mockeryMock->shouldReceive('getNumber')->with(2)->andReturn(2);
		$mockeryMock->shouldReceive('getNumber')->with(3)->andReturn(3);

		$this->assertEquals(4, $someObject->doubleNumber($mockeryMock, 2));
		$this->assertEquals(6, $someObject->doubleNumber($mockeryMock, 3));

	}

	function testPartialMocking() {
		$value = 3;
		$multiplier = 2;
		$result = 6;

		// PHPUnit

//		Specify Constructor Parameters
//		$phpMock = $this->getMock('Calculator', array('add'), array(1,2));

//		Do not call original constructor
		$phpMock = $this->getMock('Calculator', array('add'), array(), '', false);
		$phpMock->expects($this->exactly(2))->method('add')->will($this->returnValue($result));

		$this->assertEquals($result, $phpMock->multiply($value,$multiplier));

		// Mockery
		$mockeryMock = \Mockery::mock('Calculator[add]');
		$mockeryMock->shouldReceive('add')->andReturn($result);

		$this->assertEquals($result, $mockeryMock->multiply($value,$multiplier));

		// Mockery extended test checking parameters
		$mockeryMock2 = \Mockery::mock('Calculator[add]');
		$mockeryMock2->shouldReceive('add')->with(0,3)->andReturn(3);
		$mockeryMock2->shouldReceive('add')->with(3,3)->andReturn(6);

		$this->assertEquals($result, $mockeryMock2->multiply($value,$multiplier));
	}

	function testMockeryConstructorParameters() {
		$result = 6;
		// Mockery
		// Do not call constructor
		$noConstrucCall = \Mockery::mock('Calculator[add]');
		$noConstrucCall->shouldReceive('add')->andReturn($result);

		// Use constructor parameters
		$withConstructParams = \Mockery::mock('Calculator[add]', array(1,2));
		$withConstructParams->shouldReceive('add')->andReturn($result);

		// User real object with real values and mock over it
		$realCalculator = new Calculator(1,2);
		$mockRealObj = \Mockery::mock($realCalculator);
		$mockRealObj->shouldReceive('add')->andReturn($result);
	}

}

class AClassToBeMocked {

	function someMethod() {

	}

	function anotherMethod() {

	}

	function getNumber($number) {
		return $number;
	}

}

class SomeClass {

	function doSomething($anotherObject) {
		return $anotherObject->someMethod();
	}

	function concatenate($anotherObject) {
		return $anotherObject->someMethod() . ' ' . $anotherObject->someMethod();
	}

	function concatWithMinus($anotherObject) {
		return $anotherObject->anotherMethod() . ' - ' . $anotherObject->anotherMethod();
	}

	function doubleNumber($anotherObject, $number) {
		return $anotherObject->getNumber($number) * 2;
	}

}

class Calculator {
	public $myNumbers = array();

	function __construct($firstNo, $secondNo) {
		$this->myNumbers[]=$firstNo;
		$this->myNumbers[]=$secondNo;
	}

	function add($firstNo, $secondNo) {
		return $firstNo + $secondNo;
	}

	function subtract($firstNo, $secondNo) {
		return $firstNo - $secondNo;
	}

	function multiply($value, $multiplier) {
		$newValue = 0;
		for($i=0;$i<$multiplier;$i++)
			$newValue = $this->add($newValue, $value);
		return $newValue;
	}
}

?>
